#include "schedule_view.hpp"
#include <fmt/chrono.h>
#include <cassert>
#include "app/app.hpp"
#include "app/app_base.hpp"
#include "data/sql_stores.hpp"
#include "event/events.hpp"
#include "fmt/format.h"
#include "gui/controls/custom_controls.hpp"
#include "gui/controls/redmine_controls.hpp"
#include "gui/utils/window_helper.hpp"
#include "imgui/IconsFontAwesome4.h"
#include "imgui/imgui.h"
#include "imgui/imgui_stdlib.h"
#include "sql/types.hpp"
#include "utils/app_utils.hpp"
#include "utils/date_utils.hpp"

struct ScheduleView::MyData {
    void Header ();
    void Content ();

    void DrawEditor (WorkTask *work_task);
    void StartTask (WorkTask *work_task);
    void PauseTask (WorkTask *work_task);
    void FinishTask (WorkTask *work_task);
    void DeleteTask (WorkTask *work_task);
    void ShowTaskStatistics (WorkTask *work_task);
    void ExportTimeEntryToDiary(bool change_tab) const;

    bool HasTempTask() const;

    void               LoadTasks ();
    static std::string GetDateTime ();
    static int         CalcDuration (const std::string &start, const std::string &end);

    static bool LeftButtonEnabled (const WorkTask &t, int idx);
    static bool RightButtonEnabled (const WorkTask &t, int idx);

    Listener listener{App->Bus ()};

    std::string temp_title;
    float       temp_color[4]{1.f, 1.f, 1.f, 1.f};

    WorkTask                     current_task;
    TimeEntry                    current_time;
    // std::vector<WorkTask>        all_task;
    // std::vector<RedMineIssue>    all_issue;
    // std::vector<RedMineActivity> all_activity;
    // std::vector<Tag>             all_tags;

    // fields for temp task
    std::string temp_finish_time;
    std::string temp_comment;
    std::string temp_note;
    double      temp_suggest_time;
    int         temp_act_id;
    int         temp_issue_id;
    std::vector<int> temp_tags;

    bool first_frame = true;
};

template <typename T, typename F>
static int vector_index (const std::vector<T> &vec, F &&f)
{
    if (vec.empty ())
        return -1;
    const int n = static_cast<int> (std::distance (begin (vec), std::find_if (begin (vec), end (vec), [&](const T &t) {
        return f (t);
    })));
    return n == static_cast<int> (size (vec)) ? -1 : n;
}

ScheduleView::ScheduleView () :
    d{new MyData}
{
    d->listener.listen<AppEvent::TaskInfoChanged> ([this](const auto) {
        d->LoadTasks ();
    });

    // d->listener.listen<AppEvent::ActivityEvent> ([this](const auto) {
    //     d->all_activity = App->Sql ()->AllRedMineActivities ();
    // });

    // d->listener.listen<AppEvent::IssueEvent> ([this](const auto) {
    //     d->all_issue = App->Sql ()->AllRedMineIssues ();
    // });

    // d->listener.listen<AppEvent::TagEvent> ([this](const auto) {
    //     d->all_tags = App->Sql ()->AllTags ();
    // });
}

ScheduleView::~ScheduleView ()
{
    delete d;
}

void ScheduleView::Tab () const
{
    d->Header ();
    ImGui::PushStyleColor (ImGuiCol_FrameBg, IM_COL32 (0, 0, 0, 0));
    ImGui::BeginChild (ImGui::GetID (this), ImVec2{0, 0}, ImGuiChildFlags_FrameStyle);
    d->Content ();
    ImGui::EndChild ();
    ImGui::PopStyleColor (1);
}

unsigned ScheduleView::Flags () const
{
    return eTab;
}

void ScheduleView::RenderContent (ComponentFlag flag)
{
    if (d->first_frame)
    {
        // d->all_activity = App->Sql ()->AllRedMineActivities ();
        // d->all_issue    = App->Sql ()->AllRedMineIssues ();
        // d->all_tags     = App->Sql ()->AllTags ();
        d->LoadTasks ();
    }
    if (flag == eTab)
        Tab ();
}

const char *ScheduleView::GetName (ComponentFlag flag)
{
    if (flag == eTab)
        return TAB_NAME_OF(AppEvent::SelectTabEvent::eTaskSchedule);
    return nullptr;
}

static constexpr const char* temp_task_buttons[]{
    ICON_FA_TASKS " 插入临时任务",
    ICON_FA_CHECK_SQUARE " 结束此临时任务"
};

void ScheduleView::MyData::Header ()
{
    ImGui::Spacing ();
    if (current_time.entry_id > -1)
    {
        const char *start_time = "未知";
        if (current_time.entry_id > -1)
        {
            start_time = current_time.begin_time.c_str ();
        }
        if (current_task.task_id > -1)
            ImGui::BulletText("任务进行中：%s（开始时间：%s）", current_task.task_desc.data(), start_time);
        else
            ImGui::BulletText("任务进行中：临时任务（开始时间：%s）", start_time);
    }
    else
    {
        ImGui::BulletText ("任务进行中：没有正在进行中的任务");
    }

    ImGui::Spacing ();
    ImGui::Separator ();
    ImGui::Spacing ();

    bool open_new_task_editor = false;
    if (ImGui::Button (ICON_FA_PLUS " 添加新任务"))
    {
        open_new_task_editor = true;
    }

    ImGui::SameLine ();
    if (ImGui::Button (ICON_FA_PAPER_PLANE " 导出今天工作内容到日志系统"))
    {
        ExportTimeEntryToDiary(true);
    }
    HelperMaker ("基于耗时信息生成当天日志（事项）。\n默认标题为任务名，耗时仅供参考，可能需要手动调整。\n\n" ICON_FA_INFO_CIRCLE " 注意：生成的事项不会立即提交到redmine系统，需要手动提交！！", true);

    if (open_new_task_editor)
    {
        ImGui::OpenPopup ("新建任务##NewTaskEditor");
    }

    float indent_width[std::size(temp_task_buttons) + 1]{ 0, 0, 0 };
    for (int i = static_cast<int> (std::size(temp_task_buttons)) - 1; i >= 0; --i)
    {
        indent_width[i] = indent_width[i + 1] + ImGui::GetStyle().FramePadding.x * 3 + ImGui::CalcTextSize(temp_task_buttons[i]).x;
    }

    float const max_x = (ImGui::GetContentRegionAvail() + ImGui::GetCursorScreenPos() - ImGui::GetWindowPos()).x;

    // 插入临时任务
    ImGui::SameLine(max_x - indent_width[0]);
    ImGui::BeginDisabled(HasTempTask());
    if (ImGui::Button(temp_task_buttons[0]))
    {
        PauseTask(&current_task);

        App->Sql()->NewTimeEntry(-1, GetDateTime()); // 添加一个 id 为 -1 的任务表示临时任务
        App->Bus()->postpone(AppEvent::TaskInfoChanged{});
    }
    ImGui::EndDisabled();

    // 结束临时任务
    bool open_finish_temp_task_dialog = false;
    ImGui::SameLine(max_x - indent_width[1]);
    ImGui::BeginDisabled(!HasTempTask());
    if (ImGui::Button(temp_task_buttons[1]))
    {
        // finish current entry
        open_finish_temp_task_dialog = true;
        // clean temp task data
        temp_finish_time = GetDateTime();
        temp_comment = fmt::format("临时任务 #{}", current_time.entry_id);
        temp_note = fmt::format ("临时任务：{} 至 {}", current_time.begin_time, temp_finish_time);
        temp_suggest_time = std::round(CalcDuration(current_time.begin_time, temp_finish_time) / 900.0) / 4;
        temp_act_id = -1;
        temp_issue_id = -1;
        temp_tags.clear();
    }
    ImGui::EndDisabled();

    if (open_finish_temp_task_dialog)
        ImGui::OpenPopup("完成临时任务##EditorTempTaskProp");

    ImGui::PlaceNextWindowCenterScreen (ImVec2{450, 280});
    if (ImGui::BeginPopupModal ("新建任务##NewTaskEditor", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove))
    {
        ImGui::AlignTextToFramePadding ();
        ImGui::Text ("任务描述：");
        ImGui::SameLine ();
        ImGui::SetNextItemWidth (250);
        ImGui::InputTextWithHint ("##TaskDesc", "在此输入任务描述", &temp_title);
        ImGui::TextUnformatted ("着色颜色：");
        ImGui::SameLine ();
        ImGui::ColorEdit4 ("##TaskColor", temp_color, ImGuiColorEditFlags_Float | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_NoInputs);
        if (ImGui::Button (ICON_FA_CHECK_CIRCLE " 确认"))
        {
            const std::string date_time = GetDateTime ();
            const ImU32       color     = static_cast<ImU32> (ImColor (temp_color[0], temp_color[1], temp_color[2], temp_color[3]));
            const WorkTask    task      = App->Sql ()->NewTask (temp_title, date_time, color);
            if (task.task_id > 0)
            {
                App->Bus ()->postpone (AppEvent::TaskInfoChanged{});
            }
            ImGui::CloseCurrentPopup ();
        }
        ImGui::SameLine ();
        if (ImGui::Button (ICON_FA_TIMES_CIRCLE " 取消"))
        {
            temp_title = "";
            ImGui::CloseCurrentPopup ();
        }
        ImGui::EndPopup ();
    }

    ImGui::PlaceNextWindowCenterScreen(ImVec2{600,750});
    if (ImGui::BeginPopupModal ("完成临时任务##EditorTempTaskProp", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove))
    {
        // 一般只读属性
        ImGui::TextUnformatted("临时任务属性：");
        ImGui::BulletText("开始时间：%s", current_time.begin_time.c_str());
        ImGui::BulletText("结束时间：%s", temp_finish_time.c_str());
        ImGui::Separator();
        ImGui::Spacing();

        // 标题
        ImGui::TextUnformatted("标题：");
        ImGui::SameLine();
        ImGui::SetNextItemWidth(450);
        ImGui::InputText("##标题", &temp_comment);
        // 耗时信息
        ImGui::TextUnformatted("用时：");
        ImGui::SameLine();
        ImGui::SetNextItemWidth(ImGui::GetFontSize ()*6);
        ImGui::InputDouble("小时", &temp_suggest_time, 0, 0, "%.2f");
        ImGui::SameLine();
        if (ImGui::Button(ICON_FA_PENCIL))
            ImGui::OpenPopup("时间编辑器");
        ImGui::AlternativeTimeEditor(&temp_suggest_time);

        // 问题
        ImGui::TextUnformatted("问题：");
        ImGui::SameLine();
        ImGui::SetNextItemWidth(450);
        //std::string issue_text = "未关联问题";
        //auto const &all_issue = SqlViewOf(RedMineIssue);
        //if (auto const idx = vector_index(all_issue, [id = temp_issue_id](auto& x) {
        //    return x.issue_id == id;
        //    }); idx > -1)
        //    issue_text = all_issue[idx].issue_name;
        //if (ImGui::BeginCombo("##关联问题", issue_text.c_str()))
        //{
        //    for (auto const& x : all_issue)
        //    {
        //        std::string issue_name = fmt::format("{}-{}", x.project_name, x.issue_name);
        //        if (ImGui::Selectable(issue_name.c_str()))
        //        {
        //            temp_issue_id = x.issue_id;
        //        }
        //        if (x.issue_id == temp_issue_id)
        //            ImGui::SetItemDefaultFocus();
        //    }
        //    ImGui::EndCombo();
        //}
        ImGui::SelectRedMineIssue(&temp_issue_id);

        // 活动
        ImGui::TextUnformatted("活动：");
        ImGui::SameLine();
        ImGui::SetNextItemWidth(450);
        //std::string act_text = "未关联活动";
        //const auto &all_activity = SqlViewOf(RedMineActivity);
        //if (auto const idx = vector_index(all_activity, [id = temp_act_id](auto& x) {
        //    return x.act_id == id;
        //    }); idx > -1)
        //    act_text = all_activity[idx].act_name;
        //if (ImGui::BeginCombo("##关联活动", act_text.c_str()))
        //{
        //    for (auto const& x : all_activity)
        //    {
        //        if (ImGui::Selectable(x.act_name.c_str()))
        //        {
        //            temp_act_id = x.act_id;
        //        }
        //        if (x.act_id == temp_act_id)
        //            ImGui::SetItemDefaultFocus();
        //    }
        //    ImGui::EndCombo();
        //}
        ImGui::SelectRedMineActivity(&temp_act_id);

        // 标签
        const auto &all_tags = SqlViewOf(Tag);
        ImGui::TextUnformatted("标签列表：");
        ImGui::SameLine();
        if (ImGui::SmallButton(ICON_FA_PLUS_SQUARE_O))
            ImGui::OpenPopup("TagList");
        ImGui::Separator();
        if (temp_tags.empty())
            ImGui::TextUnformatted(ICON_FA_INFO_CIRCLE " 暂无标签 " ICON_FA_INFO_CIRCLE);
        else
        {
            // render tags
            static const char* buttons[]
            {
                    ICON_FA_TRASH,
                    nullptr
            };

            auto valid_cnt = temp_tags.size();
            for (size_t i = 0; i < valid_cnt;)
            {
                auto const& x = temp_tags[i];
                int         idx = vector_index(all_tags, [id = x](auto& x) {
                    return x.tag_id == id;
                    });
                if (idx < 0)
                {
                    // invalid, remove it
                    std::swap(temp_tags[i], temp_tags[valid_cnt - 1]);
                    --valid_cnt;
                    continue;
                }
                const Tag& t = all_tags[idx];
                if (ImGui::RenderTag(t, buttons, [i,this](int id, void* data) {
                    const Tag* tag = static_cast<const Tag *> (data);
                    if (tag->tag_level == L_PRIMARY)
                    {
                        temp_tags.clear();
                    }
                    else
                    {
                        temp_tags[i] = -1;
                    }
                    }, (void*)&t))
                    break;
                ++i; // next
            }
            if (valid_cnt != temp_tags.size())
                temp_tags.resize(valid_cnt);
        }

        // 备注
        ImGui::Separator();
        ImGui::TextUnformatted("备注：");
        ImGui::InputTextMultiline("##Note", &temp_note, ImVec2{ -1, -ImGui::GetFrameHeightWithSpacing() - ImGui::GetStyle ().FramePadding.y });

        if (ImGui::BeginPopup ("TagList"))
        {
            ImGui::AlignTextToFramePadding();

            bool show_secondary = std::ranges::any_of(temp_tags, [this, &all_tags](const int32_t& tag_id) {
                const int idx = vector_index(all_tags, [id = tag_id](auto& tag) {
                    return tag.tag_id == id;
                    });
                return idx > -1 && all_tags[idx].tag_level == L_PRIMARY;
                });
            int show_cnt = 0;
            for (auto& x : all_tags)
            {
                bool show = (show_secondary ? x.tag_level == L_SECONDARY : x.tag_level == L_PRIMARY) // level filter
                    && vector_index(temp_tags, [id = x.tag_id](auto& tag_id) {
                    return tag_id == id;
                        }) < 0; // not exists in list
                if (show)
                {
                    std::string text = fmt::format("[{}] {}", x.tag_level == L_PRIMARY ? "主" : "次", x.tag_name);
                    if (ImGui::Selectable(text.c_str()))
                    {
                        temp_tags.emplace_back(x.tag_id);
                    }
                    ++show_cnt;
                }
            }
            if (show_cnt == 0) {
                ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 240, 0, 255));
                ImGui::Selectable(ICON_FA_FROWN_O " 已经没有可添加的标签了");
                ImGui::PopStyleColor();
            }
            ImGui::EndPopup();
        }

        static const char* btns[] = {
            ICON_FA_CHECK_CIRCLE_O " 提交",
            ICON_FA_MEH_O          " 点错了",
            ICON_FA_TIMES_CIRCLE_O " 放弃此时间段",
        };

        ImGui::WindowBottomButtons(3, btns, [this](int idx, void*) {
            if (idx == 0)
            {
                // create diary item
                AppEvent::CreateDiaryItemEvent evt;
                evt.title = temp_comment;
                evt.time = temp_suggest_time;
                evt.date = current_time.begin_time.substr (0, 10);
                evt.act_id = temp_act_id;
                evt.issue_id = temp_issue_id;
                evt.tags = temp_tags;
                evt.note = temp_note;
                App->Bus()->postpone (evt);

                SendInfo ("日记已记录");

                // update time entry
                App->Sql()->UpdateTimeEntry (current_time, temp_finish_time, (int)(temp_suggest_time*3600));
                App->Sql()->MarkTimeEntryAsExported (current_time);
                App->Bus()->postpone (AppEvent::TaskInfoChanged{});
            }
            else if (idx == 1) {
                // 点错了，啥也不做
            }
            else if (idx == 2) {
                // 放弃操作

                // update time entry
                App->Sql()->UpdateTimeEntry(current_time, temp_finish_time, (int)(temp_suggest_time * 3600));
                App->Sql()->MarkTimeEntryAsExported(current_time);
                App->Bus()->postpone(AppEvent::TaskInfoChanged{});
            }
            ImGui::CloseCurrentPopup();
        }, nullptr);

        ImGui::EndPopup();
    }
}

void ScheduleView::MyData::DrawEditor (WorkTask *work_task)
{
    static WorkTask  task_copy;
    static WorkTask *target;
    if (work_task)
    {
        target    = work_task;
        task_copy = *work_task;
    }

    ImGui::PlaceNextWindowCenterScreen (ImVec2{600, 480});
    if (ImGui::BeginPopupModal ("编辑任务##TaskEditor", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove))
    {
        // title
        ImGui::Text ("任务描述：%s", task_copy.task_desc.c_str ());

        ImGui::Spacing ();
        ImGui::Separator ();
        ImGui::Spacing ();

        // redmine issue
        ImGui::TextUnformatted ("关联问题：");
        ImGui::SameLine ();
        ImGui::SetNextItemWidth (400);
        ImGui::SelectRedMineIssue(&task_copy.redmine_issue_id);
        ImGui::Spacing ();

        // redmine activity
        ImGui::TextUnformatted ("关联活动：");
        ImGui::SameLine ();
        ImGui::SetNextItemWidth (400);
        ImGui::SelectRedMineActivity(&task_copy.redmine_act_id);
        ImGui::Spacing ();

        // color
        ImGui::TextUnformatted ("任务颜色标记：");
        ImColor color{task_copy.display_color_rgba};
        ImGui::SameLine ();
        if (ImGui::ColorEdit3 ("##TaskColor", (float *) &color,
                               ImGuiColorEditFlags_Float | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_NoInputs))
            task_copy.display_color_rgba = color;
        ImGui::Spacing ();

        // tags
        const auto &all_tags = SqlViewOf(Tag);
        ImGui::TextUnformatted ("任务标签：");
        ImGui::SameLine ();
        if (ImGui::Button (ICON_FA_PLUS_SQUARE_O " 添加标签"))
        {
            ImGui::OpenPopup ("##TagSelector");
        }
        ImGui::Separator ();
        // render tags
        static const char *buttons[]
        {
                ICON_FA_TRASH,
                nullptr
        };
        auto valid_cnt = task_copy.tags.size ();
        for (size_t i = 0; i < valid_cnt;)
        {
            auto const &x   = task_copy.tags[i];
            int         idx = vector_index (all_tags, [id=x](auto &x) {
                return x.tag_id == id;
            });
            if (idx < 0)
            {
                // invalid, remove it
                std::swap (task_copy.tags[i], task_copy.tags[valid_cnt - 1]);
                --valid_cnt;
                continue;
            }
            const Tag &t = all_tags[idx];
            if (ImGui::RenderTag (t, buttons, [i](int id, void *data) {
                const Tag *tag = (const Tag *) data;
                if (tag->tag_level == L_PRIMARY)
                {
                    task_copy.tags.clear ();
                }
                else
                {
                    task_copy.tags[i] = -1;
                }
            }, (void*)&t))
                break;
            ++i; // next
        }
        if (valid_cnt != task_copy.tags.size ())
            task_copy.tags.resize (valid_cnt);

        if (ImGui::BeginPopup ("##TagSelector"))
        {
            ImGui::AlignTextToFramePadding ();

            bool show_secondary = std::any_of (begin (task_copy.tags), end (task_copy.tags), [this, &all_tags](const int32_t &tag_id) {
                int idx = vector_index (all_tags, [id=tag_id](auto &tag) {
                    return tag.tag_id == id;
                });
                return idx > -1 && all_tags[idx].tag_level == L_PRIMARY;
            });

            int show_cnt = 0;
            for (auto &x: all_tags)
            {
                bool show = (show_secondary ? x.tag_level == L_SECONDARY : x.tag_level == L_PRIMARY) // level filter
                            && vector_index (task_copy.tags, [id = x.tag_id](auto &tag_id) {
                                return tag_id == id;
                            }) < 0; // not exists in list
                if (show)
                {
                    std::string text = fmt::format ("[{}] {}", x.tag_level == L_PRIMARY ? "主" : "次", x.tag_name);
                    if (ImGui::Selectable (text.c_str ()))
                    {
                        task_copy.tags.emplace_back (x.tag_id);
                    }
                    ++show_cnt;
                }
            }
            if (show_cnt == 0) {
                ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 240, 0, 255));
                ImGui::Selectable(ICON_FA_FROWN_O " 已经没有可添加的标签了");
                ImGui::PopStyleColor();
            }

            ImGui::EndPopup ();
        }

        ImGui::WindowBottomButtons (2, default_ok_cancel_button_text, [this](int idx, void *) {
            if (idx == 0)
            {
                // ok clicked
                App->Sql ()->UpdateTask (task_copy);
                // update to target task
                *target = task_copy;
                if (current_task.task_id == task_copy.task_id)
                    current_task = task_copy;
            }
            else if (idx == 1)
            {
                // cancel clicked, reset variables
                target = nullptr;
            }
            ImGui::CloseCurrentPopup ();
        }, nullptr);

        ImGui::EndPopup ();
    }
}

void ScheduleView::MyData::StartTask (WorkTask *work_task)
{
    const std::string date_time = GetDateTime ();
    if (current_time.entry_id > -1)
    {
        // 这里应该是任务切换，需要计算上次的时间
        const int seconds = CalcDuration (current_time.begin_time, date_time);
        if (seconds <= 0)
        {
            // 无法计算，需要提示用户
        }
        current_time.duration = seconds;
        App->Sql ()->UpdateTimeEntry (current_time, date_time, seconds < 0 ? 0 : seconds);
    }
    current_task.last_time_entry = -1;
    App->Sql ()->UpdateTask (current_task);
    const TimeEntry entry = App->Sql ()->NewTimeEntry (work_task->task_id, date_time);
    if (entry.entry_id > -1)
    {
        work_task->last_time_entry = entry.entry_id;
        App->Sql ()->UpdateTask (*work_task);
    }
    App->Sql ()->ClearActiveTask ();
    App->Sql ()->SetTaskStatus (*work_task, eInProgress);
    App->Bus ()->postpone (AppEvent::TaskInfoChanged{});
}

void ScheduleView::MyData::PauseTask (WorkTask *work_task)
{
    const std::string date_time = GetDateTime();
    if (current_time.entry_id > -1)
    {
        // 这里应该是任务切换，需要计算上次的时间
        const int seconds = CalcDuration(current_time.begin_time, date_time);
        if (seconds <= 0)
        {
            // 无法计算，需要提示用户
            SendWarning(fmt::format("无法计算上次任务的耗时信息，时间范围 {} 至 {}", current_time.begin_time, date_time));
        }
        current_time.duration = seconds;
        App->Sql()->UpdateTimeEntry(current_time, date_time, seconds < 0 ? 0 : seconds);
    }
    current_task.last_time_entry = -1;
    App->Sql()->UpdateTask(current_task);
    App->Sql ()->SetTaskStatus (*work_task, ePaused);
    current_task = App->Sql ()->GetCurrentTask ();
    current_time.entry_id = -1;
}

void ScheduleView::MyData::FinishTask (WorkTask *work_task)
{
    App->Sql ()->SetTaskStatus (*work_task, eFinished);
    current_task = App->Sql ()->GetCurrentTask ();
}

void ScheduleView::MyData::DeleteTask (WorkTask *work_task)
{
    // before delete, we need sync all not exported time_entries
    ExportTimeEntryToDiary(false);

    App->Sql ()->DeleteTask (*work_task);
    App->Bus ()->postpone (AppEvent::TaskInfoChanged{});
}

void ScheduleView::MyData::ShowTaskStatistics (WorkTask *work_task)
{
    // TODO: Implementations
}

void ScheduleView::MyData::ExportTimeEntryToDiary (bool change_tab) const
{
    auto today = GetToday();
    auto entries = App->Sql()->GetTimeEntries(today, today);

    int count = 0;
    const auto &all_task = SqlViewOf(WorkTask);
    for (auto& item : entries) {
        if (item.exported)
            continue;
        int idx = vector_index(all_task, [id = item.task_id](auto& tsk){ return tsk.task_id == id; });
        if (idx > -1) {
            auto const& tsk = all_task[idx];
            AppEvent::CreateDiaryItemEvent evt;
            evt.date = item.begin_time.substr(0, 10);
            evt.note = fmt::format("从 {} 到 {}， 共 {} 秒。", item.begin_time, item.end_time, item.duration);
            evt.time = std::round((double)item.duration / 900) / 4; // 向 15min 四舍五入
            evt.title = "任务：" + tsk.task_desc;
            evt.act_id = tsk.redmine_act_id;
            evt.issue_id = tsk.redmine_issue_id;
            evt.tags = tsk.tags;

            App->Sql()->MarkTimeEntryAsExported(item);
            App->Bus()->postpone(evt);
            ++count;
        }
    }

    if (count > 0)
        SendInfo(fmt::format("已添加{}个事项于{}节点下", count, today));

    if (change_tab)
        App->Bus()->postpone(AppEvent::SelectTabEvent{ .which = AppEvent::SelectTabEvent::eDiary });
}

bool ScheduleView::MyData::HasTempTask () const
{
    return (current_time.entry_id >= 0 && current_task.task_id == -1);
}

bool ScheduleView::MyData::LeftButtonEnabled (const WorkTask &t, int idx)
{
    switch (idx)
    {
    case 0: return t.status == ePaused;
    case 1: return t.status == eInProgress;
    case 2: return t.status == ePaused;
    default: return true;
    }
}

bool ScheduleView::MyData::RightButtonEnabled (const WorkTask &t, int idx)
{
    if (idx == 2)
    {
        return t.status != eInProgress;
    }
    return true;
}

static constexpr const char *left_button_text[]{
        ICON_FA_PLAY " 开始",
        ICON_FA_PAUSE " 暂停",
        ICON_FA_CHECK " 完成",
};

static constexpr const ImU32 left_button_color[]{
        IM_COL32 (103, 215, 103, 255),
        IM_COL32 (157, 137, 52, 255),
        IM_COL32 (202, 148, 246, 255),
};

static_assert(std::size (left_button_text) == std::size (left_button_color));

static constexpr const char *right_button_text[]{
        ICON_FA_AREA_CHART " 统计",
        ICON_FA_PENCIL " 编辑",
        ICON_FA_TRASH " 删除",
};

static constexpr const ImU32 right_button_color[]{
        0,
        0,
        IM_COL32 (255, 0, 0, 255),
};

static_assert(std::size (right_button_text) == std::size (right_button_color));

void ScheduleView::MyData::Content ()
{
    auto &all_task = SqlViewOfMut(WorkTask);
    const auto &all_activity = SqlViewOf(RedMineActivity);
    const auto &all_issue = SqlViewOf(RedMineIssue);
    const auto &all_tags = SqlViewOf(Tag);
    float indent_width[std::size (right_button_text) + 1]{0, 0, 0, 0};
    for (int i = static_cast<int> (std::size (right_button_text)) - 1; i >= 0; --i)
    {
        indent_width[i] = indent_width[i + 1] + ImGui::GetStyle ().FramePadding.x * 3 + ImGui::CalcTextSize (right_button_text[i]).x;
    }
    const ImGuiID task_editor_view_id = ImGui::GetID ("编辑任务##TaskEditor");
    ImGui::BeginDisabled (HasTempTask());
    WorkTask *    work_task           = nullptr;
    for (size_t i = 0; i < all_task.size (); ++i)
    {
        auto &t = all_task[i];
        ImGui::PushID (static_cast<int> (i));
        ImGui::PushStyleColor (ImGuiCol_Header, t.display_color_rgba);
        ImGui::PushStyleColor (ImGuiCol_HeaderHovered, t.display_color_rgba);
        ImGui::PushStyleColor (ImGuiCol_HeaderActive, t.display_color_rgba);
        if (ImGui::CollapsingHeader (t.task_desc.c_str (), ImGuiTreeNodeFlags_DefaultOpen))
        {
            ImGui::BulletText ("创建时间：%s", t.create_date.c_str ());
            int act_idx = vector_index (all_activity, [id = t.redmine_act_id](auto &x) {
                return x.act_id == id;
            });
            int issue_idx = vector_index (all_issue, [id = t.redmine_issue_id](auto &x) {
                return x.issue_id == id;
            });
            ImGui::BulletText ("关联问题：%s", issue_idx >= 0 ? all_issue[issue_idx].issue_name.c_str () : "尚未设置关联问题");
            ImGui::BulletText ("关联活动：%s", act_idx >= 0 ? all_activity[act_idx].act_name.c_str () : "尚未设置关联活动");
            if (t.tags.empty ())
                ImGui::BulletText ("任务标签：暂无标签");
            else
            {
                ImGui::BulletText ("任务标签：");
                for (auto tag: t.tags)
                {
                    const int idx = vector_index (all_tags, [id = tag](auto &x) {
                        return id == x.tag_id;
                    });
                    if (idx >= 0)
                    {
                        const Tag &tt = all_tags[idx];
                        ImGui::SameLine ();
                        ImGui::PushStyleColor (ImGuiCol_Text, tt.tag_color);
                        ImGui::Text ("[%s]%s", tt.tag_level == L_PRIMARY ? "主" : "次", tt.tag_name.c_str ());
                        ImGui::PopStyleColor ();
                    }
                }
            }
            ImGui::Spacing ();

            int active = -1;
            // left-side buttons
            for (int j = 0; j < static_cast<int> (std::size (left_button_text)); ++j)
            {
                if (j != 0)
                    ImGui::SameLine ();
                ImGui::BeginDisabled (!LeftButtonEnabled (t, j));
                if (left_button_color[j] != 0)
                {
                    ImGui::PushStyleColor (ImGuiCol_Button, left_button_color[j]);
                    if (ImGui::Button (left_button_text[j]))
                    {
                        work_task = &t;
                        active    = j;
                    }
                    ImGui::PopStyleColor ();
                }
                else
                {
                    if (ImGui::Button (left_button_text[j]))
                    {
                        work_task = &t;
                        active    = j;
                    }
                }
                ImGui::EndDisabled ();
            }
            switch (active)
            {
            case 0: StartTask (work_task);
                break; // start
            case 1: PauseTask (work_task);
                break; // pause
            case 2: FinishTask (work_task);
                break;      // finish
            default: break; // no button
            }

            float const max_x = (ImGui::GetContentRegionAvail() + ImGui::GetCursorScreenPos() - ImGui::GetWindowPos()).x;

            // right-side buttons
            active = -1;
            for (int j = 0; j < static_cast<int> (std::size (right_button_text)); ++j)
            {
                ImGui::BeginDisabled (!RightButtonEnabled (t, j));
                ImGui::SameLine (max_x - indent_width[j]);
                if (right_button_color[j] != 0)
                {
                    ImGui::PushStyleColor (ImGuiCol_Button, right_button_color[j]);
                    if (ImGui::Button (right_button_text[j]))
                    {
                        work_task = &t;
                        active    = j;
                    }
                    ImGui::PopStyleColor ();
                }
                else
                {
                    if (ImGui::Button (right_button_text[j]))
                    {
                        work_task = &t;
                        active    = j;
                    }
                }
                ImGui::EndDisabled ();
            }
            switch (active)
            {
            case 0: ShowTaskStatistics (work_task);
                break; // statistics
            case 1: ImGui::OpenPopup (task_editor_view_id);
                break; // edit
            case 2: DeleteTask (work_task);
                break;      // delete
            default: break; // no button
            }

            ImGui::Separator ();
            ImGui::Spacing ();
        }
        ImGui::PopStyleColor (3);
        ImGui::PopID ();
    }
    ImGui::EndDisabled();
    // Editor
    DrawEditor (work_task);
}

void ScheduleView::MyData::LoadTasks ()
{
    current_task = App->Sql ()->GetCurrentTask ();
    current_time = App->Sql ()->GetCurrentTimeEntry (current_task.last_time_entry);
    // all_task     = App->Sql ()->GetTasks ();
    first_frame  = false;
}

std::string ScheduleView::MyData::GetDateTime ()
{
    return fmt::format ("{:%Y-%m-%d %H:%M:%S}", fmt::localtime (std::chrono::system_clock::now ()));
}

int ScheduleView::MyData::CalcDuration (const std::string &start, const std::string &end)
{
    static const auto extract_time = [](const std::string &time_str, int(&value)[3]) {
        try
        {
            value[0] = std::stoi(time_str.substr(11, 2));
            value[1] = std::stoi(time_str.substr(14, 2));
            value[2] = std::stoi(time_str.substr(17, 2));
        } catch (...) {}
    };
    // format is yyyy-mm-dd hh:MM:ss
    // only calc if date part is same
    if (start.substr(0, 10) == end.substr(0, 10))
    {
        // 日期一致，可以计算
        int time1[3]{ 0 }, time2[3]{ 0 };
        extract_time(start, time1);
        extract_time(end, time2);
        int time_diff[3]{ 0 };
        time_diff[0] = time2[0] - time1[0];
        time_diff[1] = time2[1] - time1[1];
        time_diff[2] = time2[2] - time1[2];

        if (time_diff[2] < 0)
        {
            time_diff[2] += 60;
            --time_diff[1];
        }

        if (time_diff[1] < 0)
        {
            time_diff[1] += 60;
            --time_diff[0];
        }

        const int result = time_diff[0] * 3600 + time_diff[1] * 60 + time_diff[2];
        assert(result >= 0);
        return result;
    }
    return 0; // 不支持
}
